package tcplib

import (
	"errors"
	"io"
	"log"
	"net"
	"sync"
	"time"
)

// 会话事件回调处理
type SessionHandler interface {
	// 会话建立
	TcpSessionCreated(session *TcpSession) bool
	// 会话断开
	TcpSessionClosed(session *TcpSession)
	// 会话数据
	TcpSessionReceived(session *TcpSession, data []byte) bool
}

// 会话
type TcpSession struct {
	conn    net.Conn       // tcp连接
	handler SessionHandler // 回调
	wait    sync.WaitGroup // 等待
	quit    chan struct{}  // 退出信号
}

// 新建会话，需传入事件回调
func TcpSessionStart(conn net.Conn, handler SessionHandler) *TcpSession {
	session := &TcpSession{
		conn:    conn,
		handler: handler,
		quit:    make(chan struct{}),
	}
	if handler.TcpSessionCreated(session) {
		session.wait.Add(1)
		go func() {
			defer session.wait.Done()
			session.service()
		}()
		return session
	}
	return nil
}

// 返回会话网络连接
func (s *TcpSession) Conn() net.Conn {
	return s.conn
}

// 会话发送数据
func (s *TcpSession) Write(buf []byte) (n int, err error) {
	if s.conn == nil {
		return 0, errors.New("no connection")
	}
	s.conn.SetWriteDeadline(time.Now().Add(1000 * time.Millisecond))
	n, err = s.conn.Write(buf)
	if err != nil {
		log.Println("TcpSession write err:", err)
	}
	return n, err
}

// 关闭会话
func (s *TcpSession) Stop() {
	// 退出服务
	close(s.quit)
	// 等待服务退出
	s.wait.Wait()
}

// 等待会话结束
func (s *TcpSession) Wait() {
	s.wait.Wait()
}

// 服务
func (s *TcpSession) service() {
	defer func() {
		s.handler.TcpSessionClosed(s)
		s.conn.Close()
		s.conn = nil
		// log.Println("##TcpSession service exit")
	}()
	buf := make([]byte, 2048)
ReadLoop:
	for {
		select {
		case <-s.quit:
			// 强制退出，不打印log
			return
		default:
			s.conn.SetDeadline(time.Now().Add(200 * time.Millisecond))
			n, err := s.conn.Read(buf)
			if err != nil {
				if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
					continue ReadLoop
				} else if err != io.EOF {
					log.Println("read error", err)
					return
				}
			}
			if n == 0 {
				return
			}
			// log.Printf("received from %v: %s", s.conn.RemoteAddr(), string(buf[:n]))
			if !s.handler.TcpSessionReceived(s, buf[:n]) {
				return
			}
		}
	}
}
